<template>
  <div ref="dom" class="deviceModel" v-loading="loading" element-loading-background="rgba(255, 255, 255, 0)"
    :fullscreen="false">

  </div>
</template>
<script setup lang="ts">
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';/** 轨道控制器 */
import { RGBELoader } from 'three/examples/jsm/loaders/RGBELoader.js';/** 图片加载器 */
import { GUI } from 'three/examples/jsm/libs/lil-gui.module.min.js';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';

import { nextTick, onBeforeUnmount, onMounted, ref } from 'vue'
import ThreeConfig from '@/types/three.ts'
import { Water } from 'three/examples/jsm/objects/Water2.js';
/** @api */
interface Props {
  config: ThreeConfig
}
// 组件传参
const props = withDefaults(defineProps<Props>(), {});
/** @数据 */
const loading = ref<boolean>(false);
const dom = ref<HTMLDivElement>();
let renderer: THREE.WebGLRenderer | null = null;
let scene: THREE.Scene | null = null;
let camera: THREE.PerspectiveCamera;
let controls: OrbitControls;
let onWindowResize: any;
let animationId: number = 0
/** @生命周期 */
onMounted(() => {
  nextTick(() => {
    baseThree();
  })
})
onBeforeUnmount(() => {
  renderer?.dispose();
  renderer = null;
  scene = null;
  window.removeEventListener('resize', onWindowResize);
  cancelAnimationFrame(animationId);
})
/** @方法 */
// 创建three
const baseThree = async () => {
  loading.value = true;
  let width = dom.value?.clientWidth ?? 0;
  let height = dom.value?.clientHeight ?? 0;
  /** @初始化场景 */
  scene = new THREE.Scene();
  /** @初始化渲染器 */
  renderer = new THREE.WebGLRenderer({ antialias: true });// 设置抗锯齿
  renderer.setSize(width, height);
  renderer.setClearColor(0xffffff, 0); //渲染色为空白
  renderer.toneMapping = THREE.ACESFilmicToneMapping; //设置电影效果获得更好的渲染效果
  renderer.toneMappingExposure = 0.5;// 色调映射的曝光级别
  renderer.shadowMap.enabled = true; //允许在场景中使用阴影贴图
  dom.value?.appendChild(renderer.domElement);
  /** @创建主相机 */
  camera = new THREE.PerspectiveCamera(75, width / height, 1, 10000);
  // 相机位置
  camera.position.set(props.config.camera.x, props.config.camera.y, props.config.camera.z);
  /** @初始化控制器 */
  controls = new OrbitControls(camera, renderer.domElement);
  controls.target.set(props.config.center.x, props.config.center.y, props.config.center.z);
  controls.enableDamping = true; // 阻尼惯性
  controls.dampingFactor = 0.08; // 阻尼惯性有多大(默认0.5)
  controls.autoRotate = true; // 自动围绕目标旋转
  /** @加载环境 */
  const rgbeLoader = new RGBELoader();
  const bg = new URL('@/assets/equirectangular/001.hdr', import.meta.url).href;
  rgbeLoader.load(bg, (envMap) => {
    envMap.mapping = THREE.EquirectangularReflectionMapping;// 设置球形映射
    // if (scene) scene.background = envMap; // 设置环境贴图
    // if (scene) scene.backgroundBlurriness = 1;
    if (scene) scene.environment = envMap; // 设置场景环境贴图
  })
  /** @监听屏幕变化 */
  onWindowResize = window.addEventListener("resize", () => {
    width = dom.value?.clientWidth ?? 0;
    height = dom.value?.clientHeight ?? 0;
    renderer?.setSize(width, height); // 重置渲染器宽高比
    camera.aspect = width / height; // 重置相机宽高比
    camera.updateProjectionMatrix(); // 更新摄像机投影矩阵。在任何参数被改变以后必须被调用。
  })
  /** @渲染函数 */
  const animate = () => {
    controls.update();
    animationId = requestAnimationFrame(animate);
    renderer && renderer.render(scene as THREE.Scene, camera);
  }
  animate();
  const { modelScene } = await addmodel();

  /** @调试模式 */
  DebugFun(modelScene);
}
/** 添加模型 */
const addmodel = async () => {
  const gltfLoader = new GLTFLoader();
  const gltf = await gltfLoader.loadAsync(import.meta.env.BASE_URL + props.config.model.url);
  const modelScene = gltf.scene;
  modelScene.scale.set(props.config.model.scale, props.config.model.scale, props.config.model.scale);
  modelScene.position.set(props.config.model.position.x, props.config.model.position.y, props.config.model.position.z);
  loading.value = false;
  modelScene.name = 'plantModel'
  modelScene.children.forEach(item => {
    if (item.name === '平面') item.visible = false;
  })
  scene?.add(modelScene);
  scene?.children.forEach(item => { item.visible = true })
  return { modelScene }
}
/** @调试模式 */
const DebugFun = (model?: THREE.Group<THREE.Object3DEventMap>, waterList?: Water[]) => {
  if (props.config.Debug === true) {
    // 一.辅助线
    const axesHelper = new THREE.AxesHelper(10000);
    (scene as THREE.Scene).add(axesHelper); // 坐标轴
    // 二.辅助格子
    const gridHelper = new THREE.GridHelper(10000, 100);
    gridHelper.material.opacity = 0.4;  // 辅助线
    gridHelper.material.transparent = false;
    (scene as THREE.Scene).add(gridHelper);
    /** 三.创建GUI */
    const gui = new GUI();
    // 1.相机配置
    const xiangji = gui.addFolder('相机');
    xiangji.add(camera.position, 'x', 1).onChange(() => {
      camera.updateProjectionMatrix();
    })
    xiangji.add(camera.position, 'y', 1).onChange(() => {
      camera.updateProjectionMatrix();
    })
    xiangji.add(camera.position, 'z', 1).onChange(() => {
      camera.updateProjectionMatrix();
    })
    // 2.模型设置
    if (model) {
      const moxing = gui.addFolder('模型');
      const modelAddress = moxing.addFolder('位置')
      const modelScale = moxing.addFolder('缩放')
      modelAddress.add(model.position, 'x', -Infinity, Infinity, 1)
      modelAddress.add(model.position, 'y', -Infinity, Infinity, 1)
      modelAddress.add(model.position, 'z', -Infinity, Infinity, 1)
      const data = { scale: props.config.model.scale }
      modelScale.add(data, 'scale', -Infinity, Infinity, 0.01).onChange((val) => {
        model.scale.set(val, val, val)
      })
    }
    /** 旋转中心点 */
    const zhongxin = gui.addFolder('中心点')
    zhongxin.add(props.config.center, 'x').onChange(() => {
      controls.target.set(props.config.center.x, props.config.center.y, props.config.center.z);
    })
    zhongxin.add(props.config.center, 'y').onChange(() => {
      controls.target.set(props.config.center.x, props.config.center.y, props.config.center.z);
    })
    zhongxin.add(props.config.center, 'z').onChange(() => {
      controls.target.set(props.config.center.x, props.config.center.y, props.config.center.z);
    })
    if (props.config.model.water && waterList) {
      const shui = gui.addFolder('水')
      waterList.forEach((_item, i) => {
        const guisui = shui.addFolder('水' + i);
        const yanse = guisui.addFolder('颜色' + i);
        const weizhi = guisui.addFolder('位置' + i)
        const daxiao = guisui.addFolder('大小' + i)
        yanse.addColor(props.config.model.water[i], 'color').onChange((val) => {
          waterList[i].material.uniforms.color.value.set(val);
        })
        weizhi.add(props.config.model.water[i].position, 'x').onChange((val) => {
          waterList[i].position.x = val
        })
        weizhi.add(props.config.model.water[i].position, 'y').onChange((val) => {
          waterList[i].position.y = val
        })
        weizhi.add(props.config.model.water[i].position, 'z').onChange((val) => {
          waterList[i].position.z = val
        })
        daxiao.add(props.config.model.water[i].size, 'x').onChange(() => {
          const waterGeometry = new THREE.BoxGeometry(props.config.model.water[i].size.x, props.config.model.water[i].size.y, props.config.model.water[i].size.z);
          waterList[i].geometry = waterGeometry
        })
        daxiao.add(props.config.model.water[i].size, 'y').onChange(() => {
          const waterGeometry = new THREE.BoxGeometry(props.config.model.water[i].size.x, props.config.model.water[i].size.y, props.config.model.water[i].size.z);
          waterList[i].geometry = waterGeometry
        })
        daxiao.add(props.config.model.water[i].size, 'z').onChange(() => {
          const waterGeometry = new THREE.BoxGeometry(props.config.model.water[i].size.x, props.config.model.water[i].size.y, props.config.model.water[i].size.z);
          waterList[i].geometry = waterGeometry
        })
      })
    }
    // 三.点位配置
    const xinxi = gui.addFolder('信息窗口');
    props.config.infoList.forEach((item) => {
      const info = xinxi.addFolder(item.name);
      info.add(item.position, 'x')
      info.add(item.position, 'y')
      info.add(item.position, 'z')
    })
  }
}
/** @侦听器 */
/** @公共方法 */
</script>
<style lang="scss" scoped>
.deviceModel {
  width: 622px;
  height: 338px;
  //background: url('@/assets/imgs/device.png') no-repeat center center;
  //background-size: 390px 369px;
}
</style>